package com.sq.universe.base.conf;

import com.sq.universe.base.constants.BaseConstants;
import com.sq.universe.base.service.AuthorityService;
import com.sq.universe.base.utils.MD5Utils;
import com.sq.universe.system.entity.RoleMenuEntity;
import com.sq.universe.system.entity.UserDepartmentEntity;
import com.sq.universe.system.entity.UserEntity;
import com.sq.universe.system.entity.UserRoleEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;


@Slf4j
public class CustomShiroRealm extends AuthorizingRealm {

    @Autowired
    private AuthorityService authorityService;


    private static HashedCredentialsMatcher hc;

    static {
        hc = new HashedCredentialsMatcher();
        hc.setHashIterations(3);
        hc.setHashAlgorithmName(Md5Hash.ALGORITHM_NAME);
    }

    public CustomShiroRealm(){
        super(new MemoryConstrainedCacheManager(),hc);
    }


    /**
     * create by: leigq
     * description: 授权
     * create time: 2019/7/1 10:32
     *
     * @return 权限信息，包括角色以及权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //如果身份认证的时候没有传入User对象，这里只能取到userName
        //也就是SimpleAuthenticationInfo构造的时候第一个参数传递需要User对象
        UserEntity user = new UserEntity();
        user.setUserLoginName((String)principals.getPrimaryPrincipal());
        user = authorityService.getUser(user);
        // 查询用户角色，一个用户可能有多个角色
        UserRoleEntity userRoleEntity = new UserRoleEntity();
        userRoleEntity.setUrUserId(user.getUserId());
        List<UserRoleEntity> roles = authorityService.queryUserRole(userRoleEntity);
        Map menuMap = new HashMap();
        for (UserRoleEntity role : roles) {
            authorizationInfo.addRole(role.getRole().getRoleCode());
            RoleMenuEntity roleMenuEntity = new RoleMenuEntity();
            roleMenuEntity.setRmRoleCode(role.getRole().getRoleCode());
            // 根据角色查询权限
            authorizationInfo.addRole(role.getUrRoleCode());
            List<RoleMenuEntity> roleMenuList = authorityService.queryRoleMenu(roleMenuEntity);
            for (RoleMenuEntity roleMenu : roleMenuList) {
                //过滤掉上级权限
                if(roleMenu.getRmMenuCode().split(":").length == 3){
                    authorizationInfo.addStringPermission(roleMenu.getRmMenuCode());
                    menuMap.put(roleMenu.getRmMenuCode(),roleMenu.getMenu().getMenuName());
                }
            }
        }
        SecurityUtils.getSubject().getSession().setAttribute(BaseConstants.CURRENT_USER_AUTH,menuMap);
        return authorizationInfo;
    }

    /**
     * create by: leigq
     * description: 主要是用来进行身份认证的，也就是说验证用户输入的账号和密码是否正确。
     * create time: 2019/7/1 09:04
     *
     * @return 身份验证信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        log.warn("开始进行身份认证......");

        //获取用户的输入的账号.
        String userName = (String) token.getPrincipal();
        UserEntity userEntity = new UserEntity();
        userEntity.setUserLoginName(userName);
        userEntity = authorityService.getUser(userEntity);

        UserRoleEntity userRoleEntity = new UserRoleEntity();
        userRoleEntity.setUrUserId(userEntity.getUserId());
        List<UserRoleEntity> userRoles = authorityService.queryUserRole(userRoleEntity);


        UserDepartmentEntity userDepartmentEntity = new UserDepartmentEntity();
        userDepartmentEntity.setUdUserId(userEntity.getUserId());
        List<UserDepartmentEntity> userDepartmentEntities = authorityService.queryUserDepartment(userDepartmentEntity);
        //通过username从数据库中查找 User对象.
        //实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro自己也是有时间间隔机制，2分钟内不会重复执行该方法
        if (Objects.isNull(userEntity)) {
            return null;
        }

        SecurityUtils.getSubject().getSession().setAttribute(BaseConstants.CURRENT_USER,userEntity);
        SecurityUtils.getSubject().getSession().setAttribute(BaseConstants.CURRENT_USER_ROLES,userRoles);
        return new SimpleAuthenticationInfo(
                // 这里传入的是user对象，比对的是用户名，直接传入用户名也没错，但是在授权部分就需要自己重新从数据库里取权限
                userName,
                // 密码
                MD5Utils.stringToMD5(userEntity.getUserPassword(),userEntity.getUserLoginName()),
                //加盐
                ByteSource.Util.bytes(userEntity.getUserLoginName()),
                // realm name
                getName()
        );
    }
}